package com.algorithm.Map;

import java.util.Objects;

public class LinkedHashMap<K,V> extends HashMap<K,V>{
    private LinkedNode<K,V> head;
    private LinkedNode<K,V> last;

    @Override
    public void clear() {
        super.clear();
        head = last = null;
    }

    @Override
    public boolean containsValue(V value) {
        LinkedNode node = head;
        while (node != null) {
            if(Objects.equals(node.value,value)) return true;
            node = node.next;
        }
        return false;
    }

    @Override
    protected void afterRemove(Node<K, V> willNode, Node<K, V> removeNode) {
        LinkedNode<K,V> node1 = (LinkedNode<K,V>)removeNode;
        LinkedNode<K,V> node2 = (LinkedNode<K,V>)willNode;
        if (willNode != removeNode){
            LinkedNode tmp = node1.prev;
            node1.prev = node2.prev;
            node2.prev = tmp;
            if (node1.prev == null) {
                head = node1;
            }else {
                node1.prev.next = node1;
            }

            if (node2.prev == null) {
                head = node2;
            }else {
                node2.prev.next = node2;
            }

            tmp = node1.next;
            node1.next = node2.next;
            node2.next = tmp;
            if (node1.next == null) {
                last = node1;
            }else {
                node1.next.prev = node1;
            }

            if (node2.next == null) {
                last = node2;
            }else {
                node2.next.prev = node2;
            }
        }

        LinkedNode<K,V> prev = node2.prev;
        LinkedNode<K,V> next = node2.next;
        if (prev == null) {
            head = next;
        }else {
            prev.next = next;
        }

        if (next == null) {
            last = prev;
        }else {
            next.prev = prev;
        }
    }

    @Override
    protected Node<K, V> createNode(K key, V value, Node<K, V> parent) {
        LinkedNode<K,V> newNode = new LinkedNode<>(key,value,parent);
        if (head == null) {
            head = last = newNode;
        }else {
            last.next = newNode;
            newNode.prev = last;
            last = newNode;
        }
        return newNode;
    }

    @Override
    public void traversal(Visitor<K, V> visitor) {
        if (head == null || visitor == null) return;
        LinkedNode<K,V> node = head;
        while (node != null) {
            if (visitor.visit(node.key,node.value)) return;
            node = node.next;
        }
    }

    private static class LinkedNode<K,V> extends Node<K,V> {
        LinkedNode next;
        LinkedNode prev;

        LinkedNode(K key, V value, Node<K, V> parent) {
            super(key, value, parent);
        }

        @Override
        public String toString() {
            return getKey(this);//getKey(prev)+ "_" + getKey(this) + "_" + getKey(next);
        }

        private String getKey(LinkedNode node) {
            return node == null ? "null" : node.key.toString();
        }

    }


    @Override
    public String toString() {
        LinkedNode node = head;
        StringBuilder stringBuilder = new StringBuilder();
        while (node != null) {
            node = node.next;
            stringBuilder.append(node);
            if (node != null) stringBuilder.append("->");
        }
        return stringBuilder.toString();
    }
}
